Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[H1410101] iP #555

Open
wants to merge 46 commits into
base: master
Choose a base branch
from
Open

[H1410101] iP #555

wants to merge 46 commits into from

Conversation

H1410101
Copy link

@H1410101 H1410101 commented Sep 4, 2023

For class reasons; ignore.

CatBot

You might be wondering, why would you use CatBot?

  • it has cats
  1. There is no better reason, and there needs no other reason.

It features peak programmer syntax, like this amazing, lovable monstrosity little guy.

Here's a snippet for your convenience:

BiConsumer<String, BiFunction<
                NamedParameterMap, BiConsumer<ErrorIndicatorIo.InvalidArgumentState, NamedParameterMap>,
                Optional<Task>>>
                createTaskIfValidElseWarn = (args, bifunction) -> slashPattern.ifParsableElseDefault(args,
                        namedParameterMap -> bifunction.apply(
                                namedParameterMap,
                                io::indicateArgumentInvalid
                        ).ifPresent(task -> {
                            taskList.addTask(task);
                            io.displayTaskAdded(taskList);
                        })
                );

        commands.addCommand("todo",
                        args -> createTaskIfValidElseWarn.accept(args, Task.Todo::createIfValidElse)
                )
                .addCommand("event",
                        args -> createTaskIfValidElseWarn.accept(args, Task.Event::createIfValidElse)
                )
                .addCommand("deadline",
                        args -> createTaskIfValidElseWarn.accept(args, Task.Deadline::createIfValidElse)
            );

That's right! Your 👀 do not deceive you. That is a

  • BiConsumer that takes in a
  • BiFunction, which takes in a
  • BiConsumer as one of its arguments
  • AND returns an Optional!

I bet you've never seen that many angle brackets in a row since CS2030S.

Programs must be written for machines to execute, and only incidentally for people to partially comprehend in flashes of brilliance after large swathes of agony.
~ Me, 2023, while descending into the infinite abyss, 2 weeks behind schedule

Copy link

@rayyan35p rayyan35p left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good code quality mostly, just need to add header comments and rename variables to make it clear. 💯

Comment on lines 27 to 92
private static void addSupportedCommandsToCommandMap() {
CommandPattern<Integer> integerPattern = CatBotCommandPatterns.getIntegerPatternGenerator()
.generate(io::indicateInvalidInteger);
CommandPattern<NamedParameterMap> slashPattern = CatBotCommandPatterns.getSlashPatternGenerator()
.generate(CatBotCommandPatterns.NO_DEFAULT);

commands.setDefaultCommand(io::indicateInvalidCommand)
.addCommand("bye", args -> io.cleanup())
.addCommand("list", args -> io.printTaskList(taskList));

// User modifying existing tasks (through IntegerPattern, and Index)
BiConsumer<String, Consumer<Integer>> runIfValidIndexElseIndicateError =
(args, lambda) -> integerPattern.ifParsableElseDefault(args,
integer -> taskList.ifValidIndexElse(integer,
lambda,
invalidIndex -> io.indicateInvalidIndex(invalidIndex, taskList.getIndexBounds())
));

//noinspection SpellCheckingInspection
commands.addCommand("mark",
string -> runIfValidIndexElseIndicateError.accept(string,
validIndex -> {
taskList.markTask(validIndex-1);
io.printTaskModified(taskList, validIndex);
}
)
)
.addCommand("unmark",
string -> runIfValidIndexElseIndicateError.accept(string,
validIndex -> {
taskList.unmarkTask(validIndex-1);
io.printTaskModified(taskList, validIndex);
}
)
)
.addCommand("delete",
string -> runIfValidIndexElseIndicateError.accept(string,
validIndex -> io.printTaskDeleted(taskList.removeTask(validIndex-1))
)
);

// User creating new tasks (with SlashPattern)

BiConsumer<String, BiFunction<NamedParameterMap, BiConsumer<ErrorIndicatorIo.InvalidState, NamedParameterMap>, Optional<Task>>>
createTaskIfValidElseWarn = (args, bifunction) -> slashPattern.ifParsableElseDefault(args,
namedParameterMap -> bifunction.apply(
namedParameterMap,
io::indicateArgumentInvalid
).ifPresent(task -> {
taskList.addTask(task);
io.printTaskAdded(taskList);
})
);

commands.addCommand("todo",
args -> createTaskIfValidElseWarn.accept(args, Task.Todo::createIfValidElse)
)
.addCommand("event",
args -> createTaskIfValidElseWarn.accept(args, Task.Event::createIfValidElse)
)
.addCommand("deadline",
args -> createTaskIfValidElseWarn.accept(args, Task.Deadline::createIfValidElse)
)
;

}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps you could have added this to another class like CommandMap?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually spent quite a bit deliberating this, but I ended up not doing it (at least for now).

The part where I defend myself

My thought process was that I wanted to add bot-specific things in bot-specific classes; to change what commands are supported and what behaviours result from commands, my current iteration only requires that I change or even replace CatBot (and possibly CatBotConsoleIO) with minimal rewrite.
While there might be some merit in a CatBotCommands class that contains this method, that would leave CatBot as a small, anaemic class (I am leaning towards this being a bad thing, but I'm not too sure of myself here).
Ultimately, I had decided that it made more sense to me to have the commands be clear from the bot description, instead of its own separate class, especially since the class would otherwise be so small as to be trivial.

The part where I stop defending myself

I definitely see an alternative that takes your considerations into account. I could rename the current file to CatBotMain or CatBotEntrypoint to reflect its explicit purpose as a small and trivial class, put this initialization as a protected static method of CatBotCommandLoader, or have it be the default initialization of CatBotCommandMap.
I'd love to hear your thoughts on why this design might be preferable, though; or if you had a different design in mind, I'd like to chew on that as well.

Regardless, thanks for the feedback!


import java.util.function.Consumer;

public class CatBotCommandPatterns {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you can consider adding header comments/javadocs? For all your other classes as well

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup! I'll get around to it. Eventually. :')

* @param map to store the mapping between command and argument
*/
private static void parseCmdArg(String s, NamedParameterMap map) {
String[] cmdArg = s.split("\\s", 2);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps you can write out the variable name of cmdArg to be in full so that it is clearer?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, will do. Thanks!

Comment on lines 71 to 76
if (emptyArgument) {
map.addNamedParameter("", s.trim());
} else {
parseCmdArg(s, map);
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that you still wrapped in curly brackets even though they are single statement!

Copy link

@remuslum remuslum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job 💯


// User creating new tasks (with SlashPattern)

BiConsumer<String, BiFunction<NamedParameterMap, BiConsumer<ErrorIndicatorIo.InvalidState, NamedParameterMap>, Optional<Task>>>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps u can split this line into 2? I think this line has 135 characters which exceeds the 120 characters limit stated in the coding standard.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, and thanks for all the nudges for coding style.
I ended up running it through a stylechecker (finally), but I appreciate the pointers!

Comment on lines 85 to 93
if (this.endOfCommand == null) findCommand();
if (this.endOfCommand == -1) return retrieve();
else return retrieve().substring(0, this.endOfCommand);
}

public String getArgs() {
if (this.endOfCommand == null) findCommand();
if (this.endOfCommand == -1) return "";
else return retrieve().substring(this.endOfCommand+1);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it would be better to have the conditionals on a separate line. E.g
if (this.endOfCommand == null) {
findCommand();
}

Comment on lines 80 to 81
if (retrieve() != null)
this.endOfCommand = retrieve().indexOf(" ");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to have curly brackets here based on the java coding standard.

Copy link

@nubnubyas nubnubyas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job for the chatbot! I really like your usage of java.util.function in your code. Just some small coding standard problems here and there. Overall, I really like your IP.

Copy link

@btaanish btaanish left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall a nice attempt with a close adherence to the coding standars. Just a few nits here and there but generally well designed.

.generate(io::indicateInvalidInteger);
CommandPattern<NamedParameterMap> slashPattern = CatBotCommandPatterns.getSlashPatternGenerator()
.generate(CatBotCommandPatterns.NO_DEFAULT);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like how you name the variables in a greater explanatory sense

return by(delimiter, false);
}

public static Parser by(String delimiter, boolean emptyArgument) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps adding javadocs for public functions might help explain their functioning in greater detail?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted! I'll probably get back to it at some point.

//endregion

//region Slash Arguments Pattern
private static final SlashArgumentPatternGenerator slashPatternGenerator = new SlashArgumentPatternGenerator();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps having shorter variable names may increase the readability of the code?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, but these are very infrequently used, so I thought it might have been better to make it very descriptive instead.
I'll think about it, but as of right now I'm still leaning towards my current convention, even if it results in excessively long variable names.

return s.replaceAll("(^|\n)", "$1"+prefix);
}

private void line() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps adding a verb in the function name might be more intuitive?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch; I'll do that!

return removed;
}

public void ifValidIndexElse(int index, Consumer<Integer> ifValid, Consumer<Integer> otherwise) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps adding context to the variable names might communicate their purpose better?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is heavily inspired by Optional's ifPresentOrElse (and other similar methods), and I'm trying to take it to its logical extensions; if, even with context, this does not seem intuitive enough, I'm happy to discuss about how to make it better!

H1410101 and others added 21 commits September 15, 2023 11:12
# Conflicts:
#	Tasks.txt
#	src/main/java/catbot/bot/CatBot.java
# Conflicts:
#	src/main/java/catbot/io/CatBotConsoleIo.java
#	src/main/java/catbot/io/UserIo.java
#	src/main/java/catbot/task/TaskList.java
Mainly added javadocs for classes, and their constructors. Also included
javadocs for all public functions.
Private or protected functions were not thoroughly documented.

Test classes were not fixed, and not updated for the new functionality.
Went in to be more thorough with code quality.
I already wrote most of the things with code quality in mind, so this is
not a very huge change.
Added comments where I deliberately diverged from general code quality
advice.
Assertions, but it's actually an extension.
# Conflicts:
#	src/main/java/catbot/io/CatBotConsoleIo.java
Code Quality, but I actually didn't do much
Especially the linguistic form of the starting word, which I did not pay
attention to while coding.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants